﻿#include "stdafx.h"
#include "skinmenubar.h"
#include "SkinMenuItem.h"
#include "WindowSkin.h"

static CSkinMenuBar*	g_pMenuBar = NULL;
static HHOOK		g_hMsgHook = NULL;

/******************************************
* 函数名 : MenuInputFilter
* 功能	 : 
*******************************************/
LRESULT CALLBACK
CSkinMenuBar::MenuInputFilter(int code, WPARAM wp, LPARAM lp)
{
	return (code==MSGF_MENU && g_pMenuBar &&
		g_pMenuBar->OnMenuInput(*((MSG*)lp))) ? TRUE
		: CallNextHookEx(g_hMsgHook, code, wp, lp);
}
/******************************************
* 函数名 : CSkinMenuBar
* 功能	 : 
*******************************************/
CSkinMenuBar::CSkinMenuBar(void)
{
	m_pHot		= NULL;
	m_iTrackingState = TRACK_NONE;		 // initial state: not tracking 
	m_iPopupTracking = m_iNewPopup = -1; // invalid


	m_pSkinWindow = GetSkin().GetWindowSkin();
	_InitCommonResources();
}
/******************************************
* 函数名 : CSkinMenuBar
* 功能	 : 
*******************************************/
CSkinMenuBar::~CSkinMenuBar(void)
{
	DeleteItems();
}
/******************************************
* 函数名 : InitItems
* 功能	 : 
*******************************************/
BOOL CSkinMenuBar::InitItems()
{
	ASSERT(m_hMenu);
	// clean up all items
	DeleteItems();
	// buttons

	CRect rtWindow;
	GetWindowRect(m_hWnd,rtWindow);
	rtWindow.OffsetRect(-rtWindow.left,-rtWindow.top);

	CRect			rtMargins = m_pSkinWindow->GetMargins();
	CRect			rtMenu;	
	m_rtPosition.left	= rtWindow.left + rtMargins.left;
	m_rtPosition.top	= rtWindow.top + rtMargins.top;
	m_rtPosition.right	= rtWindow.right - rtMargins.right;
	m_rtPosition.bottom	= rtWindow.top + rtMargins.top + GetSystemMetrics(SM_CYMENU);

	CRect rtItem ( m_rtPosition.left + 2, m_rtPosition.top + 1, m_rtPosition.right - 2, m_rtPosition.bottom - 1 );

	//int i = ::GetMenuItemCount(m_hMenu);
	for (int i = 0; i < ::GetMenuItemCount(m_hMenu); ++i) {
		CMenuButton *pButton = new CMenuButton(m_hMenu, i);
		m_arrItem.Add(pButton);
		pButton->Layout(CPoint(rtItem.left ,rtItem.top),true);
		CSize size = pButton->GetHorizontalSize();
		rtItem.left = rtItem.left + size.cx;
	}
	return TRUE;
}
/******************************************
* 函数名 : DeleteItems
* 功能	 : 
*******************************************/
void CSkinMenuBar::DeleteItems()
{
	for(int i = 0; i < m_arrItem.GetSize(); ++i) {
		CMenuItem* pItem = m_arrItem[i];
		delete pItem;
	}

	m_arrItem.RemoveAll();
}
/******************************************
* 函数名 : DeleteItems
* 功能	 : 
*******************************************/
int CSkinMenuBar::HitTestOnTrack( const CPoint& point )
{
	for (int i = 0; i < GetItemCount(); ++i) {
		CMenuItem* pItem = m_arrItem[i];
		CRect rcItem = pItem->GetRect();

		if (rcItem.PtInRect(point))
			return i;
	}

	return -1;
}
/******************************************
* 函数名 : GetIndex
* 功能	 : 
*******************************************/
CMenuItem* CSkinMenuBar::GetIndex(int nIndex) const
{
	if(IsValidIndex(nIndex))
		return m_arrItem[nIndex];
	else
		return NULL;
}
/******************************************
* 函数名 : SetMenu
* 功能	 : 
*******************************************/
void CSkinMenuBar::SetMenu(HMENU hMenu,HWND hWnd)
{
	m_hMenu = hMenu;
	if(hWnd != NULL)
		m_hWnd = hWnd;
	if ( !m_hMenu ) return;
	InitItems();
}

/******************************************
* 函数名 : TrackPopup
* 功能	 : 
*******************************************/
void CSkinMenuBar::TrackPopup(int iButton)
{

	CMenu menu;
	menu.Attach(m_hMenu);
	int nMenuItems = menu.GetMenuItemCount();

	while (iButton >= 0) {					 // while user selects another menu
		m_iNewPopup = -1;						 // assume quit after this
		m_pPressed = GetIndex(iButton);		 // press the button
	
		DrawMenuBar();						 // and force repaint now

		// post a simulated arrow-down into the message stream
		// so TrackPopupMenu will read it and move to the first item
	//	PostMessage(m_hWnd,WM_KEYDOWN, VK_DOWN, 1);
	//	PostMessage(m_hWnd,WM_KEYUP, VK_DOWN, 1);

		SetTrackingState(TRACK_POPUP, iButton); // enter tracking state


		ASSERT(g_pMenuBar == NULL);
		g_pMenuBar = this;
		ASSERT(g_hMsgHook == NULL);
		g_hMsgHook = SetWindowsHookEx(WH_MSGFILTER,
			MenuInputFilter, NULL, ::GetCurrentThreadId());


		CMenuButton *pItem= (CMenuButton*)GetIndex(iButton);
		m_hMenuTracking = pItem->m_hSubMenu;
		CRect rtWindow;
		GetWindowRect(m_hWnd,&rtWindow);
		CRect rc = pItem->m_rcItem;
		rc.OffsetRect(CPoint(rtWindow.left,rtWindow.top));
		CPoint pt ;
		pt.x = rc.left;
		pt.y = rc.bottom;
		BOOL bRet = TrackPopupMenuEx(m_hMenuTracking,
			TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL,
			pt.x, pt.y, m_hWnd, NULL);
		m_hMenuTracking = NULL;

		// uninstall hook.
		::UnhookWindowsHookEx(g_hMsgHook);
		g_hMsgHook = NULL;
		g_pMenuBar = NULL;


		m_pPressed = NULL;	 // un-press button

		DrawMenuBar();
		
		// If the user exited the menu loop by pressing Escape,
		// return to track-button state; otherwise normal non-tracking state.
		SetTrackingState(m_bEscapeWasPressed ?
				TRACK_BUTTON : TRACK_NONE, iButton);

		// If the user moved mouse to a new top-level popup (eg from File to
		// Edit button), I will have posted a WM_CANCELMODE to quit
		// the first popup, and set m_iNewPopup to the new menu to show.
		// Otherwise, m_iNewPopup will be -1 as set above.
		// So just set iButton to the next popup menu and keep looping...
		iButton = m_iNewPopup;
	}
	menu.Detach();

}
/******************************************
* 函数名 : OnMenuInput
* 功能	 : 
*******************************************/
BOOL CSkinMenuBar::OnMenuInput(MSG& m)
{
	ASSERT(m_iTrackingState == TRACK_POPUP); // sanity check
	int msg = m.message;

	if (msg==WM_KEYDOWN) 
	{
		// handle left/right-arow.
		TCHAR vkey = m.wParam;
		if ((vkey == VK_LEFT  && m_bProcessLeftArrow) ||
			(vkey == VK_RIGHT && m_bProcessRightArrow)) 
		{
			CancelMenuAndTrackNewOne(
				GetNextOrPrevButton(m_iPopupTracking, vkey==VK_LEFT));
			return TRUE; // eat it
		} 
		else if (vkey == VK_ESCAPE) 
		{
			m_bEscapeWasPressed = TRUE;	 // (menu will abort itself)
		}

	}
	else if (msg==WM_MOUSEMOVE || msg==WM_LBUTTONDOWN) 
	{
		// handle mouse move or click
		CPoint pt = m.lParam;
		CRect rtWindow;
		GetWindowRect(m_hWnd,&rtWindow);
		pt.Offset(-rtWindow.left,-rtWindow.top);

		if (msg == WM_MOUSEMOVE) 
		{
			if (pt != m_ptMouse) 
			{
				int iButton = HitTestOnTrack(pt);
				if (IsValidIndex(iButton) && iButton != m_iPopupTracking) 
				{
					// user moved mouse over a different button: track its popup
					CancelMenuAndTrackNewOne(iButton);
				}
				m_ptMouse = pt;
			}

		} 
		else if (msg == WM_LBUTTONDOWN) 
		{
			if (HitTestOnTrack(pt) == m_iPopupTracking) 
			{
				// user clicked on same button I am tracking: cancel menu
				CancelMenuAndTrackNewOne(-1);
				return TRUE; // eat it
			}
		}
	}
	return FALSE; // not handled
}
/******************************************
* 函数名 : SetTrackingState
* 功能	 : 
*******************************************/
void CSkinMenuBar::SetTrackingState(TRACKINGSTATE iState, int iButton)
{

	if (iState != m_iTrackingState) {
		if (iState == TRACK_NONE)
			iButton = -1;
	//	SetHotItem(iButton);					 // could be none (-1)
		m_pHot = GetIndex(iButton);

		if (iState==TRACK_POPUP) {
			// set related state stuff
			m_bEscapeWasPressed = FALSE;	 // assume Esc key not pressed
			m_bProcessRightArrow =			 // assume left/right arrow..
				m_bProcessLeftArrow = TRUE; // ..will move to prev/next popup
			m_iPopupTracking = iButton;	 // which popup I'm tracking
		}
		m_iTrackingState = iState;
	}
}

/******************************************
* 函数名 : GetNextOrPrevButton
* 功能	 : 
*******************************************/
int CSkinMenuBar::GetNextOrPrevButton(int iButton, BOOL bPrev)
{
	if (bPrev) {
		iButton--;
		if (iButton <0)
			iButton = GetItemCount() - 1;
	} else {
		iButton++;
		if (iButton >= GetItemCount())
			iButton = 0;
	}
	return iButton;
}
/******************************************
* 函数名 : ToggleTrackButtonMode
* 功能	 : 
*******************************************/
void CSkinMenuBar::ToggleTrackButtonMode()
{

	if (m_iTrackingState == TRACK_NONE || m_iTrackingState == TRACK_BUTTON) {
		SetTrackingState(m_iTrackingState == TRACK_NONE ?
					TRACK_BUTTON : TRACK_NONE, 0);
	}
}
/******************************************
* 函数名 : CancelMenuAndTrackNewOne
* 功能	 : 
*******************************************/
void CSkinMenuBar::CancelMenuAndTrackNewOne(int iNewPopup)
{
	if (iNewPopup != m_iPopupTracking) {
		PostMessage(m_hWnd,WM_CANCELMODE,0,0); // quit menu loop
		m_iNewPopup = iNewPopup;					 // go to this popup (-1 = quit)
	}
}
/******************************************
* 函数名 : OnLButtonDown
* 功能	 : 
*******************************************/
void CSkinMenuBar::OnLButtonDown( UINT nFlags, const CPoint& point )
{
	int iButton = HitTestOnTrack(point);
	if (iButton >= 0 && iButton<GetItemCount()) // if mouse is over a button:
		TrackPopup(iButton);
}
/******************************************
* 函数名 : OnMouseMove
* 功能	 : 
*******************************************/
void CSkinMenuBar::OnMouseMove( UINT nFlags, const CPoint& pt )
{
	CWindowDC dc(CWnd::FromHandle(m_hWnd));
	
	if(m_iTrackingState != TRACK_POPUP)
	{
		int iHot = HitTestOnTrack(pt);
		if(iHot >= 0)
		{
			m_iTrackingState = TRACK_BUTTON;
			if (IsValidIndex(iHot) && pt != m_ptMouse)
			{
				m_pHot = GetIndex(iHot);
			}
			else
				m_pHot = NULL;
		}
		else
			m_pHot = NULL;
		m_ptMouse = pt; // remember point
		DrawMenuBar();
	}
}
/******************************************
* 函数名 : CalcMenuBarPos
* 功能	 : 
*******************************************/
void CSkinMenuBar::CalcMenuBarPos()
{
	CRect rtWindow;
	GetWindowRect(m_hWnd,rtWindow);
	rtWindow.OffsetRect(-rtWindow.left,-rtWindow.top);

	CRect			rtMargins = m_pSkinWindow->GetMargins();
	CRect			rtMenu;	
	m_rtPosition.left	= rtWindow.left + rtMargins.left;
	m_rtPosition.top	= rtWindow.top + rtMargins.top;
	m_rtPosition.right	= rtWindow.right - rtMargins.right;
	m_rtPosition.bottom	= rtWindow.top + rtMargins.top + GetSystemMetrics(SM_CYMENU);
}
/******************************************
* 函数名 : DrawMenuBar
* 功能	 : 
*******************************************/
void CSkinMenuBar::DrawMenuBar()
{
	CWnd *pWnd = CWnd::FromHandle(m_hWnd);
	CWindowDC dc(CWnd::FromHandle(m_hWnd));
	CRect rtWindow;
	GetWindowRect(m_hWnd,rtWindow);
	rtWindow.OffsetRect(-rtWindow.left,-rtWindow.top);

	CRect			rtMargins = m_pSkinWindow->GetMargins();
	CRect			rtMenu;	
	m_rtPosition.left	= rtWindow.left + rtMargins.left;
	m_rtPosition.top	= rtWindow.top + rtMargins.top;
	m_rtPosition.right	= rtWindow.right - rtMargins.right;
	m_rtPosition.bottom	= rtWindow.top + rtMargins.top + GetSystemMetrics(SM_CYMENU);
	CMemDC menmDC(&dc,&m_rtPosition);
	if(m_pSkinWindow)
		m_pSkinWindow->DrawImageSection(&menmDC,m_rtPosition,m_pSkinWindow->m_skinMenuBar.imageBackground);
	CRect rcItem( m_rtPosition.left + 2, m_rtPosition.top + 1, m_rtPosition.right - 2, m_rtPosition.bottom - 1 );

	if ( m_arrItem.GetSize() == 0 ) return;

	for ( int i = 0 ; i < m_arrItem.GetSize() ; i++ )
	{
		CMenuItem* pItem = m_arrItem.GetAt(i);

		if( pItem == m_pPressed)
		{
			pItem->ModifyState(MISTATE_HOT, MISTATE_PRESSED);
			pItem->Update(&menmDC);
		}
		else if(m_iTrackingState ==TRACK_BUTTON && pItem == m_pHot)
		{
			pItem->ModifyState(MISTATE_PRESSED, MISTATE_HOT);
			pItem->Update(&menmDC);
		}
		else
		{
			pItem->ModifyState(MISTATE_PRESSED|MISTATE_HOT, 0);
			pItem->Update(&menmDC);
		}
		
	}
}